/*
 * Created by Lettle on 2021/8/15.
 * QQ: 1071445082
 * github: https://github.com/Python-Lettle
 * gitee : https://gitee.com/lettle/
 */

#include "cocktail.h"

int one_token_size = sizeof (cot_token);

void cot_token_stream_add(cot_token token, struct cot_token_stream *ret) {
    if (__DEBUG_MODE__){
        printf("Token流加入一个新的Token(%d,%d,%d)\n",token.line,token.ch,token.value->type);
    }

    if (++ret->count > ret->capacity) {
        ret->capacity *= 2;
        // 容量小了
        ret->tokens = (cot_token *) realloc (ret->tokens, ret->capacity * one_token_size);
    }
    printf("ret->count = %d\n",ret->count);
    ret->tokens[ret->count-1].ch = token.ch;
    ret->tokens[ret->count-1].line = token.line;
    ret->tokens[ret->count-1].value = (cot_value *)malloc(sizeof(cot_value));
    ret->tokens[ret->count-1].value->type = token.value->type;
    ret->tokens[ret->count-1].value->cot_stack_position = token.value->cot_stack_position;
    switch (ret->tokens[ret->count-1].value->type) {
        case FLOAT_LITERAL:
            ret->tokens[ret->count-1].value->float_value = token.value->float_value;
            printf("添加一个:%f\n",ret->tokens[ret->count-1].value->float_value);
            break;
        case INTEGER_LITERAL:
            ret->tokens[ret->count-1].value->int_value = token.value->int_value;
            printf("添加一个:%d\n",ret->tokens[ret->count-1].value->int_value);
            break;
        default:
            strcpy(ret->tokens[ret->count-1].value->string_value, token.value->string_value);
            printf("添加一个:%s\n",ret->tokens[ret->count-1].value->string_value);
            break;
    }

}

// 扫描并获取token流
void cot_token_scan(FILE * fpin, struct cot_token_stream *ret)
{
    if(__DEBUG_MODE__){
        printf("Scanner start!!!\n");
    }

    /**
     * 扫描器所需
     */
    char ch = ' ';      // 取一个字符
    char str[MAX_STRING_TYPE_LEN] = "";     // 用str连接字符串
    int str_index = 0;
    // int counter = 0;    // 记录token数

    /**
     * Token属性
     */
    cot_token token = {0,0,NULL};
    token.value = malloc(sizeof (cot_value));
    int char_count = 0; // 当前字符位置
    int line = 1;       // 当前行号

    char needNext = 1;
    while (1) {
        if(needNext) {
            if((ch = fgetc(fpin)) == EOF) break;
            char_count++;
        } else {
            needNext = 1;
        }

        memset(str,0, MAX_STRING_TYPE_LEN); // 新的str, 清空原有str记录
        str_index = 0;

        //判断过滤符
        if(ch=='\n'){
            if(__TOKEN_ANA_PROCESS__)
                printf("From line %d:%d find \\n\n", line, char_count);
            line++;char_count=0;
            continue;} //行数增加

        // 过滤符号跳过
        if(IsFilter(ch)) {
            if(__TOKEN_ANA_PROCESS__)
                printf("From line %d:%d find filter\n", line, char_count);
            continue;}

        // 判断关键字或变量名
        else if(IsLetter(ch) || (ch=='_')) {
            token.ch = char_count;  // 记录当前token位置
            // 继续获取token
            while (IsLetter(ch) || IsDigit(ch) || (ch=='_')) {
                // 加入str缓存
                str[str_index++] = ch; // TODO: 这里的str不能strcat
                // 指针跳转下一个字符
                ch = fgetc(fpin); char_count++;
            }
            token.line = line;
            str[MAX_STRING_TYPE_LEN-1] = '\0';
            strcpy(token.value->string_value, str);
            // 判断是否为关键字
            if(IsKeyword(str)) {
                if (!strcmp(str,"import"))
                    token.value->type = IMPORT;
                else if (!strcmp(str,"function"))
                    token.value->type = FUNCTION;
                else if (!strcmp(str,"return"))
                    token.value->type = RETURN;
                else if (!strcmp(str,"if"))
                    token.value->type = IF;
                else if (!strcmp(str,"else"))
                    token.value->type = ELSE;
                else if (!strcmp(str,"while"))
                    token.value->type = WHILE;
                else if (!strcmp(str,"for"))
                    token.value->type = FOR;
                else if (!strcmp(str,"do"))
                    token.value->type = DO;
                else if (!strcmp(str,"continue"))
                    token.value->type = CONTINUE;
                else if (!strcmp(str,"break"))
                    token.value->type = BREAK;
                else if (!strcmp(str,"class"))
                    token.value->type = CLASS;
                else if (!strcmp(str,"True"))
                    token.value->type = TRUE;
                else if (!strcmp(str,"False"))
                    token.value->type = FALSE;
                else if (!strcmp(str,"int"))
                    token.value->type = INT;
                else if (!strcmp(str,"double"))
                    token.value->type = DOUBLE;
                else if (!strcmp(str,"string"))
                    token.value->type = STRING;
                else if (!strcmp(str,"void"))
                    token.value->type = VOID;

            }
            // 是名称
            else token.value->type = IDENTIFIER;
            cot_token_stream_add(token, ret);
        }

        // 判断是否为"包裹的字符串常量
        else if(ch=='"') {
            // 记录当前token位置
            token.ch = char_count;
            token.line = line;

            ch = fgetc(fpin); char_count++;
            while (IsFilter(ch)){
                //判断换行符
                if(ch=='\n'){
                    line++;char_count=0;
                    continue;} //行数增加
                ch = fgetc(fpin); char_count++; // 指针跳转下一个字符
            } // 过滤符号跳过

            while (ch != '"') {
                // 加入str缓存
                str[str_index] = ch;

                // 指针跳转下一个字符
                ch = fgetc(fpin); char_count++;
                while (IsFilter(ch)){
                    //判断换行符
                    if(ch=='\n'){
                        line++;char_count=0;
                        continue;} //行数增加
                    ch = fgetc(fpin); char_count++; // 指针跳转下一个字符
                } // 过滤符号跳过
            }
            str[MAX_STRING_TYPE_LEN-1] = '\0';
            strcpy(token.value->string_value, str);
            cot_token_stream_add(token, ret);
        }

        // 判断是否为数字常量
        else if(IsDigit(ch)||(ch=='.')) {
            // 记录当前token位置
            token.ch = char_count;
            token.line = line;
            int float_mode = ch=='.';
            int i=0;
            while (IsDigit(ch)||(ch=='.')) {
                if(float_mode && ch=='.') {
                    //TODO: Error-一个数字里出现多个小数点
                    return ;
                }
                // 加入str缓存
                str[str_index++] = ch;
                if(!float_mode)float_mode = ch=='.';

                // 指针跳转下一个字符
                ch = fgetc(fpin); char_count++;
                if(IsFilter(ch)){
                    break;
                }
            }
            if(float_mode) {
                token.value->float_value = atof(str);
                token.value->type = FLOAT_LITERAL;
            } else {
                token.value->int_value = atoi(str);
                token.value->type = INTEGER_LITERAL;
            }

            cot_token_stream_add(token, ret);
            needNext = 0;
            continue;
        }

        // 判断为操作符 或者 分隔符
        if(IsOperator(ch) || IsSeparater(ch)) {
            // 记录当前token位置
            token.ch = char_count;
            token.line = line;

            str[0] = ch;
            ch = fgetc(fpin); char_count++;
            // 双操作符形式
            if(IsOperator(ch)) {
                str[1] = ch; str[2] = '\0';
                if (!strcmp(str,"&&"))
                    token.value->type = LOGICAL_AND;
                else if (!strcmp(str,"||"))
                    token.value->type = LOGICAL_OR;
                else if (!strcmp(str,"=="))
                    token.value->type = EQ;
                else if (!strcmp(str,"!="))
                    token.value->type = NE;
                else if (!strcmp(str,">="))
                    token.value->type = GE;
                else if (!strcmp(str,"<="))
                    token.value->type = LE;
                strcpy(token.value->string_value, str);
            }
            // 单操作符形式
            else {
                if (!strcmp(str,"("))
                    token.value->type = LP;
                else if (!strcmp(str,")"))
                    token.value->type = RP;
                else if (!strcmp(str,"{"))
                    token.value->type = LC;
                else if (!strcmp(str,"}"))
                    token.value->type = RC;
                else if (!strcmp(str,"["))
                    token.value->type = LB;
                else if (!strcmp(str,"]"))
                    token.value->type = RB;
                else if (!strcmp(str,"="))
                    token.value->type = ASSIGN;
                else if (!strcmp(str,"!"))
                    token.value->type = INVERT;
                else if (!strcmp(str,">"))
                    token.value->type = GT;
                else if (!strcmp(str,"<"))
                    token.value->type = LT;
                else if (!strcmp(str,"+"))
                    token.value->type = ADD;
                else if (!strcmp(str,"-"))
                    token.value->type = SUB;
                else if (!strcmp(str,"*"))
                    token.value->type = MUL;
                else if (!strcmp(str,"/"))
                    token.value->type = DIV;
                else if (!strcmp(str,"%"))
                    token.value->type = MOD;
                else if (!strcmp(str,";"))
                    token.value->type = SEMICOLON;
                else if (!strcmp(str,","))
                    token.value->type = COMMA;
                else if (!strcmp(str,"."))
                    token.value->type = DOT;
                str[1] = '\0';
                strcpy(token.value->string_value, str);
                if(__DEBUG_MODE__ && __TOKEN_ANA_PROCESS__)
                    printf("From line %d:%d find filter\n", line, char_count);
            }
//            if(IsFilter(ch)) {
//                //判断换行符
//                if(ch=='\n'){
//                    if(__DEBUG_MODE__ && __TOKEN_ANA_PROCESS__)
//                        printf("From line %d:%d find \\n\n", line, char_count);
//                    line++;char_count=0;
//                    continue;} //行数增加
//                if(__DEBUG_MODE__ && __TOKEN_ANA_PROCESS__)
//                    printf("From line %d:%d find filter\n", line, char_count);
//                ch = fgetc(fpin); char_count++;
//            }
            cot_token_stream_add(token, ret);
            needNext = 0;
            continue;
        }


        // 未识别出类型, 报错
        else
            printf("Wrong token in line %d: %s\n", line, str);


    }

}